package 中等.搜索.广度优先搜索;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;

/**
 * 基因序列可以表示为一条由 8 个字符组成的字符串，其中每个字符都是 'A'、'C'、'G' 和 'T' 之一。
 * 假设我们需要调查从基因序列 start 变为 end 所发生的基因变化。一次基因变化就意味着这个基因序列中的一个字符发生了变化。
 * 例如，"AACCGGTT" --> "AACCGGTA" 就是一次基因变化。
 * 另有一个基因库 bank 记录了所有有效的基因变化，只有基因库中的基因才是有效的基因序列。
 * 给你两个基因序列 start 和 end ，以及一个基因库 bank ，请你找出并返回能够使 start 变化为
 * end 所需的最少变化次数。如果无法完成此基因变化，返回 -1 。
 * 注意：起始基因序列 start 默认是有效的，但是它并不一定会出现在基因库中。
 * <p>
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode-cn.com/problems/minimum-genetic-mutation
 */
public class 最小基因变化_433 {

    public static void main(String[] args) {

        String[] bank = {"AATTCCGG", "AACCTGGG", "AACCCCGG", "AACCTACC"};
        System.out.println(minMutation("AACCTTGG", "AATTCCGG", bank));

    }

    /**
     * 广度优先搜索
     * 需要注意bank里面的过程不一定是最优解，
     * 需要枚举每个位置可以变化的 ACGT 的所有字符串
     *
     * @param start
     * @param end
     * @param bank
     * @return
     */
    public static int minMutation(String start, String end, String[] bank) {
        int count = 0;
        StringBuilder stringBuilder;
        char[] keys = {'A', 'C', 'G', 'T'};
        Deque<String> queue = new ArrayDeque<>();
        Map<String, Boolean> bankMap = new HashMap<>();
        for (String b : bank) {
            bankMap.put(b, false);
        }
        if (!bankMap.containsKey(end)) return -1;
        queue.addLast(start);

        while (!queue.isEmpty()) {
            int size = queue.size();
            while (size-- > 0) {
                String curStr = queue.pollFirst();
                if (curStr.equals(end)) return count;
                bankMap.put(curStr, true);
                for (int i = 0; i < 8; i++) {
                    for (char key : keys) {
                        stringBuilder = new StringBuilder(curStr);
                        // 设置某个索引的字符
                        stringBuilder.setCharAt(i, key);
                        String nextStr = stringBuilder.toString();
                        if (bankMap.containsKey(nextStr) && !bankMap.get(nextStr)) {
                            queue.addLast(nextStr);
                        }
                    }
                }
            }
            count++;
        }
        return -1;
    }

}

